﻿using System;
using System.IO;
using System.Reflection;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Tabs;
using System.Globalization;

namespace HeroldIT.Dnn.Modules
{
    /// <summary>
    /// Localizes various aspects of the DotNetNuke framework using Apollo's MLAPI if available.
    /// </summary>
    public static class Localizer
    {
        #region static properties

        #region TabLocalizer

        /// <summary>
        /// Holds the lock for initializing the <see cref="tabLocalizer"/> object.
        /// </summary>
        private static readonly object tabLocalizerInitializeLock = new object();

        /// <summary>
        /// Holds whether the tabLocalizer has been initialized.
        /// </summary>
        private static bool tabLocalizerInitialized;

        /// <summary>
        /// Holds the LocalizeTab object for localizing tabs.
        /// </summary>
        private static object tabLocalizer;

        /// <summary>
        /// Gets the LocalizeTab object for localizing tabs.
        /// </summary>
        private static object TabLocalizer
        {
            get
            {
                if (!tabLocalizerInitialized && null == tabLocalizer)
                {
                    lock (tabLocalizerInitializeLock)
                    {
                        if (null == tabLocalizer)
                        {
                            try
                            {
                                tabLocalizer = AppDomain.CurrentDomain.CreateInstanceAndUnwrap(
                                    "Apollo.LocalizationApi", "Apollo.DNN_Localization.LocalizeTab");
                            }
                            catch (FileNotFoundException)
                            {
                            }
                            catch (TypeLoadException)
                            {
                            }
                            tabLocalizerInitialized = true;
                        }
                    }
                }
                return tabLocalizer;
            }
        }

        #endregion

        #region ModuleLocalizer

        /// <summary>
        /// Holds the lock for initializing the <see cref="moduleLocalizer"/> object.
        /// </summary>
        private static readonly object moduleLocalizerInitializeLock = new object();

        /// <summary>
        /// Holds whether the moduleLocalizer has been initialized.
        /// </summary>
        private static bool moduleLocalizerInitialized;

        /// <summary>
        /// Holds the ModuleLocalizationController object for localizing modules.
        /// </summary>
        private static object moduleLocalizer;

        /// <summary>
        /// Gets the ModuleLocalizationController object for localizing modules.
        /// </summary>
        private static object ModuleLocalizer
        {
            get
            {
                if (!moduleLocalizerInitialized && null == moduleLocalizer)
                {
                    lock (moduleLocalizerInitializeLock)
                    {
                        if (null == moduleLocalizer)
                        {
                            try
                            {
                                moduleLocalizer = AppDomain.CurrentDomain.CreateInstanceAndUnwrap(
                                    "Apollo.DNN.Modules.ModuleLocalization", "Apollo.DNN.Modules.ModuleLocalization.ModuleLocalizationController");
                            }
                            catch (FileNotFoundException)
                            {
                            }
                            catch (TypeLoadException)
                            {
                            }
                            moduleLocalizerInitialized = true;
                        }
                    }
                }
                return moduleLocalizer;
            }
        }

        #endregion

        /// <summary>
        /// Gets whether tab localization is available.
        /// </summary>
        public static bool TabLocalizationAvailable
        {
            get { return null != TabLocalizer; }
        }

        /// <summary>
        /// Gets whether module localization is available.
        /// </summary>
        public static bool ModuleLocalizationAvailable
        {
            get { return null != ModuleLocalizer; }
        }

        #endregion

        #region static methods

        #region tab localization

        /// <summary>
        /// Localizes the given <see cref="TabInfo"/> using the Apollo framework.
        /// </summary>
        /// <remarks>If the Apollo framework is not installed, the original <see cref="TabInfo"/> is returned.</remarks>
        /// <param name="tab">The <see cref="TabInfo"/> to localize.</param>
        /// <returns>The localized <see cref="TabInfo"/>.</returns>
        public static TabInfo LocalizeTab(TabInfo tab)
        {
            if (null == TabLocalizer)
            {
                return tab;
            }
            MethodInfo method = TabLocalizer.GetType().GetMethod("getLocalizedTab", new[] {typeof (TabInfo)});
            if (null == method)
            {
                return tab;
            }

            TabInfo localizedTab = method.Invoke(TabLocalizer, new[] {tab}) as TabInfo;
            return localizedTab;
        }

        #endregion

        #region module localization

        /// <summary>
        /// Gets the localized version of the given <see cref="ModuleInfo"/> in the current UI culture using the Apollo framework.
        /// </summary>
        /// <remarks>If the Apollo framework is not installed, the original <see cref="ModuleInfo"/> is returned.</remarks>
        /// <param name="module">The <see cref="ModuleInfo"/> to localize.</param>
        /// <returns>The localized <see cref="ModuleInfo"/>.</returns>
        public static ModuleInfo GetLocalizedModule(ModuleInfo module)
        {
            return GetLocalizedModule(module, CultureInfo.CurrentUICulture.Name);
        }

        /// <summary>
        /// Gets the localized version of the given <see cref="ModuleInfo"/> using the Apollo framework.
        /// </summary>
        /// <remarks>If the Apollo framework is not installed, the original <see cref="ModuleInfo"/> is returned.</remarks>
        /// <param name="module">The <see cref="ModuleInfo"/> to localize.</param>
        /// <param name="locale">The name of the locale to localize to.</param>
        /// <returns>The localized <see cref="ModuleInfo"/>.</returns>
        public static ModuleInfo GetLocalizedModule(ModuleInfo module, string locale)
        {
            return LocalizeModule(module, locale, false);
        }

        /// <summary>
        /// Sets the properties of the given <see cref="ModuleInfo"/> to the localized values in the current UI culture using the Apollo framework.
        /// </summary>
        /// <remarks>If the Apollo framework is not installed, nothing is changed.</remarks>
        /// <param name="module">The <see cref="ModuleInfo"/> to localize.</param>
        /// <returns>The localized <see cref="ModuleInfo"/>.</returns>
        public static void LocalizeModule(ModuleInfo module)
        {
            LocalizeModule(module, CultureInfo.CurrentUICulture.Name);
        }

        /// <summary>
        /// Sets the properties of the given <see cref="ModuleInfo"/> to the localized values in the current UI culture using the Apollo framework.
        /// </summary>
        /// <remarks>If the Apollo framework is not installed, nothing is changed.</remarks>
        /// <param name="module">The <see cref="ModuleInfo"/> to localize.</param>
        /// <param name="locale">The name of the locale to localize to.</param>
        /// <returns>The localized <see cref="ModuleInfo"/>.</returns>
        public static void LocalizeModule(ModuleInfo module, string locale)
        {
            LocalizeModule(module, locale, true);
        }

        /// <summary>
        /// Gets the localized version of the given <see cref="ModuleInfo"/> using the Apollo framework.
        /// </summary>
        /// <remarks>If the Apollo framework is not installed, the original <see cref="ModuleInfo"/> is returned.</remarks>
        /// <param name="module">The <see cref="ModuleInfo"/> to localize.</param>
        /// <param name="locale">The name of the locale to localize to.</param>
        /// <param name="inPlace">Whether to substitute localized values in place.</param>
        /// <returns>The localized <see cref="ModuleInfo"/>.</returns>
        private static ModuleInfo LocalizeModule(ModuleInfo module, string locale, bool inPlace)
        {
            if (null == ModuleLocalizer)
            {
                return module;
            }
            MethodInfo method = ModuleLocalizer.GetType().GetMethod("Get", new[] {typeof (int), typeof (string)});
            if (null == method)
            {
                return module;
            }
            object moduleLocalizationInfo = method.Invoke(ModuleLocalizer, new object[] {module.ModuleID, locale});
            if (null == moduleLocalizationInfo)
            {
                return module;
            }
            
            if (!moduleLocalizationInfo.GetType().FullName.Equals("Apollo.DNN.Modules.ModuleLocalization.ModuleLocalizationInfo"))
            {
                return module;
            }
            PropertyInfo prop = moduleLocalizationInfo.GetType().GetProperty("localizedModuleTitle", typeof (string));
            if (null == prop)
            {
                return module;
            }
            string localizedModuleTitle = prop.GetValue(moduleLocalizationInfo, new object[0]) as string;
            if (String.IsNullOrEmpty(localizedModuleTitle))
            {
                return module;
            }
            if (String.IsNullOrEmpty(localizedModuleTitle.Trim()))
            {
                return module;
            }
            if (inPlace)
            {
                module.ModuleTitle = localizedModuleTitle;
                return module;
            }

            ModuleInfo localizedModule = module.Clone();
            localizedModule.ModuleTitle = localizedModuleTitle;
            return localizedModule;
        }

        #endregion

        #endregion
    }
}